package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	count         = 0
	root          *string
	findStr       *string
	ignoreDirName *string
)

func main() {
	flagFind()
}

// flag方式查找
func flagFind() {
	// 定义命令行参数
	root = flag.String("p", "", "输入文件绝对路径")
	findStr = flag.String("f", "", "查找的字符串")
	ignoreDirName = flag.String("i", "test", "忽略的文件名称，逗号(,)分割")

	// 自定义帮助信息
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "使用方式: %s [选项]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "选项:\n")
		flag.PrintDefaults()
	}

	// 解析命令行参数
	flag.Parse()

	// 如果没有提供任何命令行参数，则打印帮助信息
	if flag.NFlag() == 0 {
		flag.Usage()
		return
	}
	fmt.Printf("---- 示例指令：-------------------------- \n")
	fmt.Println("示例指令：./release/darwin-amd64 -p /Users/xxxx/Desktop/xxxx/master/xxxx  -f UIImage -i /Pods,project.pbxproj ")
	fmt.Printf("------------------------------ \n")
	// 打印命令行参数的值
	if *root != "" && *findStr != "" {
		fmt.Printf("---- 输入参数：-------------------------- \n")
		fmt.Printf("项目路径: %s\n查找字符串为：%s\n忽略的文件名称：%s\n", *root, *findStr, *ignoreDirName)
		fmt.Printf("------------------------------ \n")
		//for k, v := range os.Args {
		//	fmt.Printf("args[%v]=[%v] \n", k, v)
		//}
		/*
			fmt.Println("开始查找....", time.Now())
			filepath.Walk(*root, walkFunc)
			fmt.Printf("%s 总共存在：%d \n", *findStr, count)
			fmt.Println("结束查找....", time.Now())
		*/

		fmt.Println("开始查找....", time.Now())
		results := make(chan string)
		var wg sync.WaitGroup
		var totalCount int64 // 全局计数器
		// 启动一个 goroutine 进行文件搜索
		wg.Add(1)
		go searchFiles(*root, "", results, &wg)

		// 启动一个 goroutine 等待所有搜索完成
		go func() {
			wg.Wait()
			close(results)
		}()
		// 从 channel 中读取结果并启动新的 goroutine 进行内容搜索
		var searchWg sync.WaitGroup
		for result := range results {
			searchWg.Add(1)
			go searchContentInFile(result, *findStr, &totalCount, &searchWg)
		}

		// 等待所有内容搜索完成
		searchWg.Wait()

		fmt.Printf("%s 总共存在：%d \n", *findStr, totalCount)
		fmt.Println("结束查找....", time.Now())

	} else { // 表示查找的路径或者查找的内容为空
		fmt.Println("查找的路径或者内容为空....")
	}
}

// args方式查找
func argsFind() {
	//root = ""
	//
	//fmt.Println("命令行格式: ./main arg1:完整文件路径 arg2:需要查找的字符串 arg3:过滤文件夹1 arg4:过滤文件夹2 ...")
	//fmt.Println("示例指令：./main /Users/xxxx/Desktop/xxxx/master/xxxx NSLog .git ")
	//argsLen := len(os.Args)
	//if argsLen != 3 {
	//	fmt.Println("参数数量不正确，参数为：")
	//	for k, v := range os.Args {
	//		fmt.Printf("args[%v]=[%v] \n", k, v)
	//	}
	//} else {
	//	root = os.Args[1]
	//	findStr = os.Args[2]
	//	fmt.Printf("项目路径: %s 查找字符串为：%s \n", root, findStr)
	//
	//	for k, v := range os.Args {
	//		fmt.Printf("args[%v]=[%v] \n", k, v)
	//	}
	//
	//	filepath.Walk(root, walkFunc)
	//	fmt.Println("NSLog 总共存在：", count)
	//}
}

func walkFunc(path string, info os.FileInfo, err error) error {
	// fmt.Println("path === ", path)
	// fmt.Println("info === ", info)

	// 过滤目录
	if info == nil {
		return nil
	}

	if info.IsDir() {
		return nil
	}
	// 打印文件名
	// fmt.Println(filepath.Base(path))

	*ignoreDirName = strings.ReplaceAll(*ignoreDirName, ",", "|")
	// fmt.Println("忽略文件为：", *ignoreDirName)
	if *ignoreDirName == "" { // 不忽略任何文件
	} else { // 忽略
		re := regexp.MustCompile(*ignoreDirName)
		if re.MatchString(path) {
			return nil
		}
	}

	f, err := os.Open(path)
	if err != nil {
		return nil
	}
	defer f.Close()

	// 每行读取
	scanner := bufio.NewScanner(f)
	// 增加缓冲区大小以处理大文件
	buf := make([]byte, 0, 64*1024)     // 64KB buffer
	scanner.Buffer(buf, 1024*1024*1024) // 最大 1G buffer

	line := 0
	for scanner.Scan() {
		// 记录所在的行
		line += 1
		tmpText := scanner.Text()
		if strings.Contains(tmpText, *findStr) {
			count += 1
			s := fmt.Sprintf("文件名称：%s  \nline: %d", path, line)
			fmt.Println(s) // 这里就可以当成字符串处理该行
			fmt.Println("----------------------------------------------")
		}
	}

	if err := scanner.Err(); err != nil {
		fmt.Printf("Error reading file %s: %v\n", path, err)
	}

	return nil
}

// goroutine 优化查找
// 搜索函数，递归搜索指定目录下的所有文件
func searchFiles(dir string, pattern string, results chan<- string, wg *sync.WaitGroup) {
	defer wg.Done()

	var innerWg sync.WaitGroup
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		*ignoreDirName = strings.ReplaceAll(*ignoreDirName, ",", "|")
		re := regexp.MustCompile(*ignoreDirName)
		// fmt.Println("忽略文件为：", *ignoreDirName)
		if *ignoreDirName == "" { // 不忽略任何文件
			innerWg.Add(1)
			go func(p string) {
				defer innerWg.Done()
				if !info.IsDir() {
					results <- p
				}
			}(path)
		} else { // 忽略
			innerWg.Add(1)
			go func(p string) {
				defer innerWg.Done()
				if re.MatchString(p) { // 表示忽略{
				} else {
					if !info.IsDir() {
						results <- p
					}
				}
			}(path)
		}

		// if !info.IsDir() && filepath.Ext(path) == pattern {
		// 	innerWg.Add(1)
		// 	go func(p string) {
		// 		defer innerWg.Done()
		// 		results <- p
		// 	}(path)
		// }
		return nil
	})
	innerWg.Wait()

	// filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
	// 	if err != nil {
	// 		return err
	// 	}

	// 	*ignoreDirName = strings.ReplaceAll(*ignoreDirName, ",", "|")
	// 	// fmt.Println("忽略文件为：", *ignoreDirName)
	// 	if *ignoreDirName == "" { // 不忽略任何文件
	// 	} else { // 忽略
	// 		re := regexp.MustCompile(*ignoreDirName)
	// 		if re.MatchString(path) { // 表示忽略
	// 			return nil
	// 		} else { // 表示不忽略
	// 			if !info.IsDir() {
	// 				results <- path
	// 			}
	// 		}
	// 	}

	// 	// if !info.IsDir() && filepath.Ext(path) == pattern {
	// 	// 	results <- path
	// 	// }
	// 	return nil
	// })
}

// 在文件中搜索指定内容并输出行号信息
func searchContentInFile(filePath string, searchContent string, totalCount *int64, wg *sync.WaitGroup) {
	defer wg.Done()

	f, err := os.Open(filePath)
	if err != nil {
		fmt.Printf("Error opening file %s: %v\n", filePath, err)
		return
	}
	defer f.Close()

	// 每行读取
	scanner := bufio.NewScanner(f)
	// 增加缓冲区大小以处理大文件
	buf := make([]byte, 0, 64*1024)     // 64KB buffer
	scanner.Buffer(buf, 1024*1024*1024) // 最大 1G buffer
	line := 0
	for scanner.Scan() {
		// 记录所在的行
		line += 1
		tmpText := scanner.Text()
		if strings.Contains(tmpText, searchContent) {
			atomic.AddInt64(totalCount, 1) // 递增全局计数器
			s := fmt.Sprintf("文件名称：%s  \nline: %d \n-----------------------------------------------", filePath, line)
			fmt.Println(s) // 这里就可以当成字符串处理该行
			// fmt.Println("----------------------------------------------")
		}
	}

	if err := scanner.Err(); err != nil {
		fmt.Printf("Error reading file %s: %v\n", filePath, err)
	}
}
